home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1993…stman Always Clicks Twice / ADC Developer CD (1993-01) (''The Postman Always Clicks Twice'')_iso / Dev.CD 199301.iso / Development Platforms / Apple II / Essentials / Technical.Notes / IIGS / TN.IIGS.105 < prev    next >
Encoding:
Text File  |  1992-07-15  |  17.2 KB  |  330 lines  |  [TEXT/GEOL]

  1. Apple II
  2. Technical Notes
  3. _____________________________________________________________________________
  4.                                                   Developer Technical Support
  5. Apple IIgs
  6. #105: We Interrupt This CPU...
  7.  
  8. Written by: Matt Deatherage                                          May 1992
  9.  
  10. This Technical Note supplements the discussion of how interrupts generally
  11. work (or don't work) on the Apple IIgs found in the Apple IIgs Firmware
  12. Reference.  It also discusses how to patch into the interrupt chain and when
  13. not to use software interrupts.
  14. _____________________________________________________________________________
  15.  
  16.  
  17. THIS NOTE IS A SUPPLEMENT
  18.  
  19. That's right, a supplement.  This is not the definitive, end-all discussion of
  20. interrupts on the Apple IIgs.  Most of the information you need to know is
  21. available, and has been for several years, in the Apple IIgs Firmware
  22. Reference.  If you're going to write an interrupt routine, you need to read
  23. Chapter 6 of the Firmware Reference.
  24.  
  25. No excuses.  If you don't have the book, buy it or borrow it.  People who use
  26. your software don't want to hear a sad story about how you wanted to spend the
  27. money on a couple of CDs instead of preventing their machine from crashing.
  28.  
  29. If you haven't read Chapter 6 of the Firmware Reference, do so before
  30. continuing; the rest of this Note will make much more sense if you're familiar
  31. with the material covered in that chapter.
  32.  
  33.  
  34. A NOTE ABOUT TIMING
  35.  
  36. There are lots of times listed in this Note, concerning how fast certain kinds
  37. of interrupts must be serviced before they're lost.  Please remember that all
  38. times listed are ideal times--actual times are likely to be shorter.  For
  39. example, a maximum response time of a millisecond means you have one
  40. millisecond from the time the peripheral asserts the /IRQ line until the
  41. interrupt must be serviced.  If interrupts are disabled for the first 750
  42. microseconds (us) of that, then your maximum response time is 250 us.  This is
  43. why we constantly remind programmers to keep interrupts disabled for
  44. absolutely the shortest time possible.  Also, all times reflecting serial or
  45. AppleTalk interrupts already take into account the serial chip's internal
  46. 3-byte buffer.
  47.  
  48.  
  49. SO WHAT THE HECK ARE ALL THOSE VECTORS?
  50.  
  51. At first, looking at all those various vectors seems pretty darned
  52. intimidating.  However, the structure becomes clearer when you think about
  53. interrupt priority.
  54.  
  55. Some microprocessors allow interrupt requests to have priorities--higher
  56. priority interrupts can interrupt lower priority ones.  The 65816 doesn't have
  57. this capability, so the best the Apple IIgs can do is check possible interrupt
  58. sources in highest-priority-first order.  For example, AppleTalk interrupts
  59. must always be processed extremely quickly--from the time an AppleTalk
  60. interrupt is asserted, someone must read the data from the SCC within a
  61. maximum of 104.167 us or data can be lost.  That's not very much time at all,
  62. especially considering that the system may have interrupts disabled, or may be
  63. running at 1 MHz speed when the interrupt fires.
  64.  
  65. Serial interrupts are next--at 19,200 baud, there's a maximum of 1.094
  66. milliseconds to read data before it's lost.  (Multiplication shows that 38,400
  67. baud has a maximum of 547 us, and 57,600 baud has a maximum delay of 273.5 us.
  68. Not much at all.)
  69.  
  70. You'd hope the Interrupt Manager in ROM would be smart enough to service
  71. AppleTalk interrupts first and serial interrupts next, and in fact that's what
  72. it does.  In fact, it services them so fast that not all the system
  73. information is saved before checking the hardware and dispatching (if
  74. necessary) to the IRQ.APTALK or IRQ.SERIAL vectors.  See Apple IIgs Technical
  75. Note #24 for more information on which system state information isn't saved
  76. before calling those vectors.
  77.  
  78. The list of interrupt priorities is on page 180 of the Firmware Reference.
  79. What's not clear from any description of interrupt handling is that each
  80. internal interrupt source's vector is only called if the Interrupt Manager
  81. determines it is the source of the interrupt.  For example, the IRQ.DSKACC
  82. vector is not called unless the user pressed Command-Control-Esc to generate
  83. the interrupt.  This insures that external interrupt handlers for slot-based
  84. peripherals are dispatched to as quickly as possible--if each vectored routine
  85. had to determine interrupt ownership, every interrupt would have significantly
  86. more overhead.
  87.  
  88. There are two additions to the priority list in the Firmware Reference--the
  89. first is also an exception to the "interrupt handlers don't have to identify
  90. the interrupt" rule.  On ROM 3 machines only, vector $E1021C (IRQ.MIDI) gets
  91. control immediately after determining the interrupt isn't an AppleTalk
  92. interrupt.  MIDI data can come in so quickly that it needs higher priority
  93. than serial interrupts.  However, to improve performance, routines called
  94. through this vector must return as fast as possible (faster would be better)
  95. to avoid delaying interrupts further down the chain, like serial interrupts.
  96. Also note that this vector doesn't exist on ROM 1.
  97.  
  98. The second addition is to the final priority, simply defined as "external
  99. slot."  The documentation doesn't clearly indicate how this works--it kind of
  100. implies this is just calling IRQ.OTHER.  In fact, if no IRQ.OTHER routine
  101. claims the interrupt, the system does some voodoo magic to switch to emulation
  102. mode and jumps through the vector at $03FE, just like all previous Apple II
  103. models.  And just like in older systems, whatever code is pointed to by $03FE
  104. must end with an RTI instruction.  This behavior is preserved for
  105. compatibility, although it is the slowest interrupt response available on the
  106. IIgs.
  107.  
  108.  
  109. GETTING CONTROL IN TIME
  110.  
  111. Passing control to external handlers isn't always quick enough for some
  112. people.  If you're writing a telecommunications program, for example, you have
  113. no more than 1.094 ms from the time a character is received to get it out of
  114. the SCC or you'll lose data at 19,200 baud.
  115.  
  116. The Interrupt Manager is a very tight piece of code--if it were running in RAM
  117. and the system was temporarily slowed down to 1 MHz, there would only be room
  118. for about two more instructions before AppleTalk would lose data.  Since
  119. AppleTalk has to be serviced within 104.2 us (as discussed previously), and
  120. since IRQ.SERIAL is called as quickly as possible after IRQ.APTALK (the only
  121. delay is if you're on ROM 3 and a non-trivial MIDI interrupt handler is
  122. installed), patching in at IRQ.SERIAL poses no problems for most high-speed
  123. communications, even up to 57,600 baud.  In other words, it's not necessary to
  124. patch any vector other than IRQ.SERIAL to achieve the results you want.
  125. The problem comes when you have external communications hardware--making it
  126. through the internal interrupt chain is too slow if your external
  127. communications hardware has the same kinds of limitation the SCC does (namely,
  128. a 3-byte internal buffer).  External vectors are only called after all the
  129. internal sources verify it's not their interrupt, and by that time your card
  130. may have lost data.
  131.  
  132. PATCHING THE MAIN INTERRUPT VECTOR
  133.  
  134. In these cases, where there is no possible way to service an interrupt in time
  135. through the Interrupt Manager's normal priority chain, and in these cases
  136. only, it's acceptable to patch out the main interrupt vector at $E10010
  137. (preferably using GetVector and SetVector with reference number $0004).  But
  138. even then, there are rules to follow.
  139.  
  140.  
  141.        1. You should duplicate the functionality of the main interrupt vector
  142.           exactly until the point where you must gain control or lose data.
  143.           For example, if your card requires that you service interrupts
  144.           within a millisecond or lose data, AppleTalk interrupts still have
  145.           higher priority over your interrupts because AppleTalk interrupts
  146.           must be serviced within 104 us.   In this example case, your code
  147.           should duplicate the functionality of the Interrupt Manager up
  148.           through and including the call to IRQ.APTALK, and then (and only
  149.           then) call your interrupt handler, where you handle the interrupt
  150.           if it's yours and pass control to the rest of the interrupt chain
  151.           if it's not.
  152.  
  153.        2. You should only service your interrupts before AppleTalk if your
  154.           interrupts require servicing in less than 104 us.  If they don't,
  155.           give AppleTalk first shot.  If they do, you must clearly inform the
  156.           user, both in documentation and on the screen, that if they proceed
  157.           with this function network services may be interrupted, and that
  158.           they may have to restart the system to restore them.  Users must
  159.           also have the option to back out and cancel at this point.  No,
  160.           this isn't a pleasant message to deliver, but it's much nicer than
  161.           to completely disconnect AppleTalk and lock up the system if it was
  162.           booted from a server.
  163.  
  164.  
  165.        3. You should only patch out the main interrupt vector when absolutely
  166.           necessary.  For example, if you're communicating with hardware that
  167.           runs at multiple speeds and only the highest speed generates
  168.           interrupts that require patching the main vector, you should not be
  169.           patching the main vector when not using that highest speed.  For
  170.           telecommunication programs, this means different interrupt handling
  171.           routines depending on baud rates.  To do this any other way lessens
  172.           the reliability of other high-speed interrupt-driven peripherals in
  173.           the system.
  174.  
  175. And remember, it's only acceptable to patch the main interrupt vector when
  176. there is no other way to service interrupts fast enough.  At all other times,
  177. even in the same program, service your interrupts in other ways.
  178.  
  179.  
  180. VECTORS VS. BINDING VS. ALLOCATING
  181.  
  182. There are three main ways to get into the IIgs interrupt-handling chain--by
  183. patching vectors directly, by using the ProDOS 8 or ProDOS 16 call
  184. ALLOC_INTERRUPT, and by using the GS/OS call BindInt.  Each behaves
  185. differently and has advantages and disadvantages.  We'll go from the highest
  186. level to the lowest in discussing them.
  187.  
  188. BINDINT--EASY TO USE, BUT NOT AS EASY TO CONTROL
  189.  
  190. BindInt's vector reference numbers (VRNs) are designed to correspond to
  191. vectors in the IIgs Interrupt Manager's chain.  Comparing the list of numbers
  192. on page 265 of GS/OS Reference to the list of vectors starting on page 266 of
  193. the Apple IIgs Firmware Reference will make this more obvious.
  194.  
  195. When you call BindInt, GS/OS replaces the address in the appropriate interrupt
  196. vector with an address inside GS/OS.  The routine it points to calls all the
  197. routines bound to that vector, including the one that was originally installed
  198. (usually the ROM's built-in SEC/RTL address).  That is, if IRQ.VBL pointed to
  199. the Miscellaneous Tools' Heartbeat Task code before a program made four
  200. separate BindInt calls to VRN $000C, then after those calls completed, IRQ.VBL
  201. would point to code inside GS/OS that called all four bound routines and the
  202. Miscellaneous Tools' Heartbeat Task code.
  203.  
  204. This is why each bound routine is told (through the microprocessor's carry
  205. flag) if one of the other routines has already claimed the interrupt and why
  206. preserving that status is important.  BindInt is a convenient way to get code
  207. time during various kinds of interrupts, but you should note that you can't
  208. control in what order bound handlers are called.
  209.  
  210. ALLOC_INTERRUPT--OLD STYLE INTERRUPT MANAGEMENT
  211.  
  212. ALLOC_INTERRUPT and the ProDOS 8 equivalent, ALLOC_INT take the address of the
  213. routine you pass and keep it in an internal table.  When an interrupt occurs,
  214. each address in the table is called in turn until one of the interrupt
  215. handlers claims it.  In older days, failure by any of the installed interrupt
  216. handlers to claim the interrupt would bring the system to a crashing
  217. halt--nowadays unclaimed interrupts are ignored by both ProDOS 8 and GS/OS.
  218.  
  219. What the manuals don't tell you is that any routine installed in this way is
  220. called after the system has jumped through address $03FE in bank zero--in
  221. other words, at the last possible chance.  For any kind of timing-sensitive
  222. interrupts, these routines are not sufficient.
  223.  
  224. The table that stores these routines is of a fixed size--ProDOS 8's table
  225. holds four routines, and GS/OS's holds 16.  If you try to install more
  226. handlers than that, you'll get an error from the operating system.
  227.  
  228. PATCHING VECTORS--HIGH LEVEL OF CONTROL, HIGH RISK
  229.  
  230. The lowest level at which you can get control is by directly patching the
  231. Interrupt Manager's vectors as documented in the Firmware Reference.  Although
  232. this lets you get control as soon as the Interrupt Manager determines which
  233. vector to call, it also carries some compatibility risks.
  234.  
  235. Any BindInt calls with VRNs that reference a vector you patch make GS/OS take
  236. your routine's address and store it internally.  This is a problem for anyone
  237. who daisy-chained into the same interrupt vector after you did--there's no
  238. good way to disconnect yourself without disconnecting everyone who patched in
  239. after you.  This is Bad.
  240.  
  241. If you patch vectors directly, you have to check the vector when you're ready
  242. to remove your routine.  If the vector doesn't still point to your address,
  243. someone else has patched into the vector after you and you can't remove
  244. yourself.  In these cases, you have to leave a "code stub" that takes no
  245. action other than passing control along to the address that was installed when
  246. you patched in, and you have to leave that code stub at the same address as
  247. your interrupt handler.  (Since you don't know who has patched the vector
  248. after you, you have no way to communicate with those programs and tell them
  249. you're going away.)
  250.  
  251. This means your interrupt handler can't be in your main program.  If it is,
  252. when GS/OS calls UserShutDown to remove your program from memory, you'll
  253. orphan one or more pointers to your interrupt handler (which doesn't exist
  254. anymore).  You must allocate memory and load your interrupt handler with a
  255. different user ID than your main program so your code stub can survive when
  256. your program quits.  Also note that this means repeated launchings of your
  257. program could leave lots and lots of code stubs in memory--so if you can find
  258. a way other than patching vectors directly, you're encouraged to use it.
  259.  
  260.  
  261. SOFTWARE INTERRUPTS--BRK AND COP
  262.  
  263. Sometimes developers forget that BRK and COP instructions are in fact software
  264. interrupts--when the IIgs's 65816 encounters one of these instructions, it
  265. goes through the same Interrupt Manager procedures that all interrupts go
  266. through.
  267.  
  268. Among other things, this means that encountering one of these instructions
  269. inside an interrupt routine will overwrite all the system's saved information
  270. (such as registers or system state variables) with new ones, meaning you'll
  271. never be able to return from the first interrupt.  This isn't too much of a
  272. problem with BRK (except when debugging interrupt routines), but a recent fad
  273. popularity for COP makes this worth mentioning.
  274.  
  275. Some developers are trying to use COP instructions for all kinds of
  276. general-purpose mechanisms, but the system is not designed to handle this.
  277. Using a COP instruction to pass control to a shell or a library routine in
  278. production-level code is not acceptable for several reasons.  First, any COP
  279. instruction inside an interrupt handler will bring the system to its knees.
  280. Second, there is no arbitration for the COP vector so multiple users of it
  281. will collide.  Third, although a COP instruction takes only two bytes, it
  282. takes many hundreds more cycles to execute than a JSL instruction, slowing the
  283. system down for no reason.
  284.  
  285. COP instructions are perfectly acceptable in non-production level (debugging)
  286. code, but developers should not use them as a way for different program
  287. modules to communicate.  Such use is not supported and is strongly discouraged
  288. by Apple.
  289.  
  290.  
  291. BEFORE WE RTI--A SUMMARY
  292.  
  293. This Note covers many issues concerning interrupts, so here's a summary.  This
  294. isn't all the explanation--refer to individual topics for discussions and
  295. reasons.
  296.  
  297.    o   Never disable interrupts for longer than necessary--you make life
  298.        really difficult on routines that rely on high-speed interrupt
  299.        capability.
  300.  
  301.    o   Interrupt routines should patch in as late as possible in the
  302.        interrupt process without losing data.  If your interrupt source
  303.        doesn't need servicing as fast as AppleTalk does, don't patch in
  304.        before AppleTalk.
  305.  
  306.    o   Patching the main interrupt vector at $E10010 is only acceptable if
  307.        there's no possible way to service external interrupts quickly enough
  308.        (internal interrupt sources, like serial ports, should always use
  309.        other vectors), and even then the vector most only be patched while
  310.        necessary; if a slower interrupt source is used in the same program,
  311.        unpatch the vector.
  312.  
  313.    o   Different methods of installing interrupt handlers give you different
  314.        levels of control.  BindInt is the overall best method, although you
  315.        can't control in what order bound routines are called.
  316.  
  317.    o   COP instructions are unacceptable in non-debugging code; they should
  318.        never take the place of JSL instructions or other methods of
  319.        inter-process communication.
  320.  
  321.  
  322. Further Reference
  323. _____________________________________________________________________________
  324.  
  325.    o   Apple IIgs Firmware Reference
  326.    o   Apple IIgs Toolbox Reference, Volume 3
  327.    o   GS/OS Reference
  328.    o   ProDOS 8 Technical Reference Manual
  329.    o   Apple IIgs Technical Note #24,  Apple IIgs Toolbox Reference updates
  330.